package main

import (
	"context"
	pb "demo/micro/grpc/proto"
	"fmt"
	"google.golang.org/grpc"
	"sync"
	"sync/atomic"
)

type grpcClient struct {
	clientMap map[string]*grpc.ClientConn //客户端连接列表
	mutex     *sync.Mutex                 //互斥锁
	balancer  LoadBalancer                //负载均衡
}

//客户端连接数量
func (c *grpcClient) ConnectionCount() int {
	return len(c.clientMap)
}

//轮询获取下一个连接  索引
func (c *grpcClient) Next() int {
	return c.balancer.choose(c.ConnectionCount())
}

func (c *grpcClient) GetClient() *grpc.ClientConn {
	clients := make([]*grpc.ClientConn, 0, len(c.clientMap))
	c.mutex.Lock()
	for _, v := range c.clientMap {
		clients = append(clients, v)
	}
	c.mutex.Unlock()

	conn := clients[c.Next()]
	fmt.Println("get client, addr=" + conn.Target())
	return conn
}

//添加连接
func (c *grpcClient) AddClient(addr string) {
	conn, err := grpc.Dial(addr, grpc.WithInsecure())
	checkErr(err)

	c.mutex.Lock()
	c.clientMap[addr] = conn
	c.mutex.Unlock()
}

func (c *grpcClient) DeleteClient(addr string) {
	c.mutex.Lock()
	delete(c.clientMap, addr)
	c.mutex.Unlock()
}

//负载均衡
type LoadBalancer interface {
	choose(n int) int
}

//轮询负载均衡
type RoundRobinLoadBalancer struct {
	index int64
}

func (r *RoundRobinLoadBalancer) choose(n int) int {
	atomic.StoreInt64(&r.index, (r.index+1)%int64(n))
	return int(r.index)
}

func InitClient(etcdAddr string, serviceName string) *grpcClient {
	//获取服务列表
	addrs, err := Discover(serviceName, etcdAddr)
	checkErr(err)
	fmt.Printf("discover service list, addrs = %v\n", addrs)

	c := new(grpcClient)                     //客户端连接列表
	c.mutex = new(sync.Mutex)                //互斥锁
	c.balancer = new(RoundRobinLoadBalancer) //负载均衡

	c.clientMap = make(map[string]*grpc.ClientConn)

	for _, addr := range addrs {
		conn, err := grpc.Dial(addr, grpc.WithInsecure())
		checkErr(err)
		c.clientMap[addr] = conn
	}

	fmt.Printf("init client=%v\n", c)

	Watch(c, serviceName)
	return c
}

func StartClientWithEtcd() {
	etcdAddr := "127.0.0.1:2379"
	serviceName := "linfangyi"

	client := InitClient(etcdAddr, serviceName)

	//轮询获取client
	conn := client.GetClient()
	c := pb.NewGreeterClient(conn)
	resp, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: "林芳伊"})
	checkErr(err)
	fmt.Println(resp.Message)

	conn1 := client.GetClient()
	c1 := pb.NewLinfangyiServiceClient(conn1)
	resp1, err := c1.Hello(context.Background(), &pb.LinfangyiRequest{Question: "name"})
	checkErr(err)
	fmt.Println(resp1.Resp)

	conn2 := client.GetClient()
	c2 := pb.NewLinfangyiServiceClient(conn2)
	resp1, err = c2.Hello(context.Background(), &pb.LinfangyiRequest{Question: "age"})
	checkErr(err)
	fmt.Println(resp1.Resp)
}
